package com.nmgydy.util;

import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.config.RequestConfig.Builder;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;

import com.alibaba.fastjson.JSON;

/** httpClient工具类 */
public class HttpUtil {

	private static final int CONNECTION_TIMEOUT = 5000;
	private static final int SOCKET_TIMEOUT = 10000;
	private static final int REQUEST_TIMEOUT = 5000;

	private static final int MAX_CONNECTION = 100;
	private static final int MAX_ROUTE_CONNECTIONS = 50;

	private static final RequestConfig defalutRequectCfg;
	private static final Object lock = new Object();

	private static HttpClientBuilder clientBuilder;
	
	private static final boolean isDebug = ConfigUtil.getBooleanValue("log.debug");
    private static final Logger logger = Logger.getLogger(HttpsClient.class);
    
	static {
		// 默认配置，设默认的连接超时时间、请求超时时间、结果超时时间
		Builder reqCfg = RequestConfig.custom();
		reqCfg.setSocketTimeout(SOCKET_TIMEOUT);
		reqCfg.setConnectTimeout(CONNECTION_TIMEOUT);
		reqCfg.setConnectionRequestTimeout(REQUEST_TIMEOUT);
		defalutRequectCfg = reqCfg.build();
	}

	private static HttpClientBuilder getClientBuilder() {
		if (clientBuilder != null) {
			return clientBuilder;
		}

		synchronized (lock) {
			if (clientBuilder != null) {
				return clientBuilder;
			}
			// 实例化manager
			RegistryBuilder<ConnectionSocketFactory> registBuilder = RegistryBuilder.<ConnectionSocketFactory> create();
			registBuilder.register("http", PlainConnectionSocketFactory.INSTANCE);
			PoolingHttpClientConnectionManager conMgr = new PoolingHttpClientConnectionManager(registBuilder.build());
			conMgr.setMaxTotal(MAX_CONNECTION);

			// 默认没有设置，高并发下回报大量的 TimeOutException
			conMgr.setDefaultMaxPerRoute(MAX_ROUTE_CONNECTIONS);
			// End Modify

			// 设置配置
			HttpClientBuilder cBuilder = HttpClientBuilder.create();
			cBuilder.setConnectionManager(conMgr);
			cBuilder.setDefaultRequestConfig(defalutRequectCfg);

			clientBuilder = cBuilder;
		}
		return clientBuilder;
	}

	/**
	 * 从pool中获取一个HttpClient对象来执行Get/Post/Delete/Put等方法
	 * 
	 * @return
	 * @throws Exception
	 */
	private static HttpClient getClient() {
		// 实例化客户端
		CloseableHttpClient client = getClientBuilder().build();
		return client;
	}

	/**
	 * 发送Get请求，参数默认
	 * 
	 * @param url
	 * @return
	 * @throws Exception
	 */
	public static HttpReturnMessage doGet(String url) {
		return doGet(url, null, null, -1);
	}

	/**
	 * 发送Get请求，带有超时时间<br>
	 * 其他默认
	 * 
	 * @param url
	 * @return
	 * @throws Exception
	 */
	public static HttpReturnMessage doGet(String url, int timeout) {
		return doGet(url, null, null, timeout);
	}

	/**
	 * 放Get请求，带有参数<br>
	 * 其他默认
	 * 
	 * @param url
	 * @param params
	 * @return
	 * @throws Exception
	 */
	public static HttpReturnMessage doGet(String url, Map<String, String> params) {
		return doGet(url, params, null, -1);
	}

	/**
	 * 发用Get请求，带有超时时间和请求参数
	 * 
	 * @param url
	 * @param params
	 * @param timeout
	 * @return
	 * @throws Exception
	 */
	public static HttpReturnMessage doGet(String url, Map<String, String> params, int timeout) {
		return doGet(url, params, null, timeout);
	}

	/**
	 * 发送Get请求，自定义所有参数<br>
	 * 
	 * @param httpUrl
	 * @param params
	 * @param headers
	 * @param timeout
	 * @return
	 * @throws Exception
	 */
	public static HttpReturnMessage doGet(String httpUrl, Map<String, String> params, Map<String, String> headers,
			int timeout) {

		// 构建Url
		URIBuilder url;
		HttpGet httpGet;
		try {
			url = new URIBuilder(httpUrl);
			// 设置请求参数
			if (params != null) {
				for (Entry<String, String> entry : params.entrySet()) {
					url.addParameter(entry.getKey(), entry.getValue());
				}
			}
			httpGet = new HttpGet(url.build());
		} catch (URISyntaxException e) {
			e.printStackTrace();
			return null;
		}
		url.setCharset(Charset.forName("UTF-8"));
		// 设置特殊的超时时间
		if (timeout > 0) {
			Builder cfg = RequestConfig.copy(defalutRequectCfg);
			cfg.setConnectTimeout(timeout);
			httpGet.setConfig(cfg.build());
		}

		// 设置header
		if (headers != null) {
			for (Entry<String, String> entry : headers.entrySet()) {
				httpGet.addHeader(entry.getKey(), entry.getValue());
			}
		}

		// 执行请求
		HttpResponse response;
		try {
			response = getClient().execute(httpGet);
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}
		
		HttpReturnMessage hrm = getHttpBody(response);
		return hrm;
	}

	/**
	 * 发送post请求，使用默认超时时间
	 * 
	 * @param url
	 * @param params
	 * @return
	 * @throws Exception
	 */
	public static HttpReturnMessage doPost(String url, Map<String, String> params) {
		return doPost(url, params, null, -1);
	}

	/**
	 * 发送post请求，带有请求参数和超时时间
	 * 
	 * @param url
	 * @param params
	 * @param timeout
	 * @return
	 * @throws Exception
	 */
	public static HttpReturnMessage doPost(String url, Map<String, String> params, int timeout) {
		return doPost(url, params, null, timeout);
	}
	
	/**
	 * 发送post请求，带有请求参数和header
	 */
	public static HttpReturnMessage doPost(String url, Map<String, String> params, Map<String, String> headers) {
		return doPost(url, params, headers, -1);
	}

	/**
	 * 发送post请求,全部参数自定义
	 * 
	 * @param url
	 * @param params
	 * @param headers
	 * @param timeout
	 * @return
	 * @throws Exception
	 */
	public static HttpReturnMessage doPost(String url, Map<String, String> params, Map<String, String> headers,
			int timeout) {

//		if (log.isDebugEnabled()) {
//			int t = timeout;
//			if (t < 0) {
//				t = REQUEST_TIMEOUT;
//			}
//			String msg = String
//					.format("Inovke Http Interface By Post Method , The URL is %s , params  = [ %s ] , headers = [ %s ] , timeout = %d",
//							url, params, headers, t);
//			log.debug(msg);
//		}

		HttpPost httpPost = new HttpPost(url);
		// 设置请求参数
		if (params != null) {
			List<NameValuePair> values = getNameValuePairList(params);
			UrlEncodedFormEntity entity = new UrlEncodedFormEntity(values, Charset.forName("UTF-8"));
			httpPost.setEntity(entity);
		}

		// 设置特殊的超时时间
		if (timeout > 0 && timeout < 120000) {
			Builder cfg = RequestConfig.copy(defalutRequectCfg);
			cfg.setConnectTimeout(timeout);
			httpPost.setConfig(cfg.build());
		} 

		if (timeout == 120000) {
			Builder cfg = RequestConfig.copy(defalutRequectCfg);
			cfg.setConnectTimeout(timeout);
			cfg.setSocketTimeout(120000);
			httpPost.setConfig(cfg.build());
		} 
		
		// 设置header
		if (headers != null) {
			for (Entry<String, String> entry : headers.entrySet()) {
				httpPost.addHeader(entry.getKey(), entry.getValue());
			}
		}

		// 执行请求
		HttpResponse response;
		HttpReturnMessage hrm;
		try {
			response = getClient().execute(httpPost);
			hrm = getHttpBody(response);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
		if(isDebug){
        	logger.info("Http Post - url:"+url+",params:"+JSON.toJSONString(params)+",response:"+hrm.getResult());
        }
		return hrm;

	}
	
	public static HttpReturnMessage doPost(String url,String paramBody){
		CloseableHttpResponse response = null;
        CloseableHttpClient client = null;
        try {
            HttpPost httpPost = new HttpPost(url);
            StringEntity entityParams = new StringEntity(paramBody,"utf-8");
            httpPost.setEntity(entityParams);
            httpPost.setHeader("Content-Type", "text/xml;charset=utf-8");
            //httpPost.setHeader("Content-Type", "text/xml;charset=ISO-8859-1");
            client = HttpClients.createDefault();
            response = client.execute(httpPost);
            HttpReturnMessage hrm = getHttpBody(response);
            return hrm;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
            	if(response != null){
                    response.close();
                }
            	if(client != null){
                    client.close();
                }
			} catch (IOException e) {
				e.printStackTrace();
			}
        }
        return null;
	}

	/**
	 * 上传文件<br>
	 * 使用Http-mine包上传<br>
	 * 默认超时时间 @see{REQUEST_TIMEOUT}
	 * 
	 * @param httpUrl
	 * @param files
	 * @param params
	 * @return
	 * @throws Exception
	 */
	public static HttpReturnMessage upload(String httpUrl, Map<String, byte[]> files, Map<String, String> params)
			throws Exception {
		return upload(httpUrl, files, params, -1);
	}

	/**
	 * 上传文件<br>
	 * 使用Http-mine包上传<br>
	 * 
	 * @param httpUrl
	 * @param files
	 * @param params
	 * @param responseListner
	 * @throws Exception
	 */
	public static HttpReturnMessage upload(String httpUrl, Map<String, byte[]> files, Map<String, String> params,
			int timeout) {

//		if (log.isDebugEnabled()) {
//			int t = timeout;
//			if (t < 0) {
//				t = REQUEST_TIMEOUT;
//			}
//			String msg = String.format(
//					"Upload File To Http Interface , Thre Url is %s , params = [ %s ] , timeout = { %d }", httpUrl,
//					params, t);
//			log.debug(msg);
//		}

		//
		MultipartEntityBuilder builder = MultipartEntityBuilder.create();
		builder.setMode(HttpMultipartMode.RFC6532);// 设置游览器兼容模式
		builder.setBoundary(UUID.randomUUID().toString());
		builder.setCharset(Charset.forName("UTF-8"));
		// 加入普通参数
		Set<Entry<String, String>> pes = params.entrySet();
		ContentType ctype = ContentType.create("text/plain", Charset.forName("UTF-8"));
		for (Entry<String, String> ps : pes) {
			StringBody stringBody = new StringBody(ps.getValue(), ctype);
			builder.addPart(ps.getKey(), stringBody);
		}
		// 加入文件内容
		Set<Entry<String, byte[]>> es = files.entrySet();
		for (Entry<String, byte[]> bs : es) {
			builder.addBinaryBody(bs.getKey(), bs.getValue(), ContentType.MULTIPART_FORM_DATA, bs.getKey());
		}
		// 实例化客户端
		HttpPost post = new HttpPost(httpUrl);
		if (timeout > 0) {// 超时配置
			Builder reqCfg = RequestConfig.copy(defalutRequectCfg);
			reqCfg.setConnectionRequestTimeout(timeout);
			post.setConfig(reqCfg.build());
		}
		// 发送
		post.setEntity(builder.build());
		HttpReturnMessage hrm;
		try {
			hrm = getHttpBody(getClient().execute(post));
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
		return hrm;
	}
	/**
	 * 重载的下载方法
	 * 
	 * @param httpUrl
	 * @param params
	 * @return
	 * @throws Exception
	 */
	public static byte[] download(String httpUrl, Map<String, String> params) {
		return download(httpUrl, params, null, -1);
	}

	/**
	 * 下载文件，输出到内存中，转换为byte数组<br>
	 * 如果大文件不建议用这种方式，很快会耗尽内存<br>
	 * @param httpUrl
	 * @param params
	 * @param headers
	 * @param timeout
	 * @throws Exception
	 */
	public static byte[] download(String httpUrl, Map<String, String> params, Map<String, String> headers, int timeout) {

		HttpPost httpPost = new HttpPost(httpUrl);
		// 设置请求参数
		if (params != null) {
			List<NameValuePair> values = getNameValuePairList(params);
			UrlEncodedFormEntity entity = new UrlEncodedFormEntity(values, Charset.forName("UTF-8"));
			httpPost.setEntity(entity);
		}
		// 执行请求
		HttpResponse response;
		try {
			response = getClient().execute(httpPost);
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}
		// 构建返回结果
		int code = response.getStatusLine().getStatusCode();
		HttpReturnMessage msg = new HttpReturnMessage(code);
		//
		HttpEntity rs = response.getEntity();
		
		try {
			// 这里如果下载失败，则status code 不能为200
			if (msg.getCode() == 200) {
				return EntityUtils.toByteArray(rs);
			} else {
				String content = EntityUtils.toString(rs, "UTF-8");
				throw new RuntimeException("Download Failed! The Code : " + code + " , Return Result : " + content);
			}
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		} finally {
			try {
				EntityUtils.consume(rs);
			} catch (IOException e) {
			}
		}
	}

	/**
	 * 获取HttpBody
	 * 
	 * @param execute
	 * @return
	 * @throws Exception
	 */
	private static HttpReturnMessage getHttpBody(HttpResponse resp) {

		int code = resp.getStatusLine().getStatusCode();
		HttpReturnMessage hrm = new HttpReturnMessage(code);

		HttpEntity rs = resp.getEntity();
		String content = null;
		try {
			content = EntityUtils.toString(rs, "UTF-8");
			// 关闭流
			EntityUtils.consume(rs);
		} catch (IOException e) {
			e.printStackTrace();
		}
		hrm.setResult(content);
		return hrm;
	}

	private static List<NameValuePair> getNameValuePairList(Map<String, String> params) {
		List<NameValuePair> listParam = new ArrayList<NameValuePair>();
		for (String key : params.keySet()) {
			listParam.add(new BasicNameValuePair(key, params.get(key)));
		}
		return listParam;
	}

}